package ru.mipt.sport.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import ru.mipt.sport.model.entity.*;
import ru.mipt.sport.model.interfaces.*;

/**
 *
 * @author danon
 */
public class DBHelper {
    
    private DBHelper() {
        // singleton!!! :)
    }
    
    synchronized public static DBHelper getInstance() {
        if(instance==null)
            instance = new DBHelper();
        return instance;
    }
    
    private static DBHelper instance;

    /**
     * Adds competitions sport with all stages to the database.
     * Transaction will be started automatically, but will NOT bit committed
     * In case of error, it will throw the exception
     * @param db {@code DBUtils} instance to access the database
     * @param sport Sport
     * @param stages list of stages
     * @return persisted state of the CompetitionSport
     */
    public CompetitionSport addCompetitionSport(DBUtils db, CompetitionEntity competition, CompetitionSport sport, Collection<CompetitionStage> stages) {
        EntityManager em = db.getEntityManager(true);
        try {
            sport.setCompetition(competition);
            em.persist(sport);
            if(stages!=null) {
                for(CompetitionStage stage : stages) {
                    stage.setSport((CompetitionSportEntity)sport);
                    if(stage.getCriteria()!=null)
                        em.persist(stage.getCriteria());
                    em.persist(stage);
                }
            }
        } catch (Exception ex) {
            System.err.println("DBHelper.addCompetitionSport : failed to persist competition sport. Returning null.");
            System.err.println("Reason: "+ex.getLocalizedMessage());
            db.rollback(); 
            throw ex;
        }
        
        return sport;
    }

    public void deleteCompetition(DBUtils db, Competition c) {
        EntityManager em = db.getEntityManager(true);
        try {
            // deleting result entries
            Query q = em.createQuery("delete from RunResultEntryEntity rr"
                    + " where rr.runResult.competitionRun.stage.sport.competition = :competition").setParameter("competition", c);
            int r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetition: "+r+" result entries deleted");
            // deleting run results
            q = em.createQuery("delete from RunResultEntity rr"
                    + " where rr.competitionRun.stage.sport.competition = :competition").setParameter("competition", c);
            r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetition: "+r+" run results deleted");
            // deleting runs
            q = em.createQuery("delete from CompetitionRunEntity r"
                    + " where r.stage.sport.competition = :competition").setParameter("competition", c);
            r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetition: "+r+" runs deleted");
            // deleting stages
            q = em.createQuery("delete from CompetitionStageEntity s "
                    + "where s.sport.competition = :competition").setParameter("competition", c);
            r=q.executeUpdate();
            System.out.println("DBHelper.deleteCompetition: "+r+" stages deleted");
            // deleting sports
            q = em.createQuery("delete from CompetitionSportEntity s "
                    + "where s.competition = :competition").setParameter("competition", c);
            r=q.executeUpdate();
            System.out.println("DBHelper.deleteCompetition: "+r+" sports deleted");
            // deleting competition
            em.remove(c);
            System.out.println("DBHelper.deleteCompetition: competition has been removed");
        } catch (Exception ex) {
            System.err.println("DBHelper.deleteCompetition: failed to remove competition.");
            System.err.println("Reason: "+ex.getLocalizedMessage());
            db.rollback();
        }
    }

    public void deleteCompetitionSport(DBUtils db, CompetitionSport s) {
        EntityManager em = db.getEntityManager(true);
        try {
            // deleting result entries
            Query q = em.createQuery("delete from RunResultEntryEntity rr"
                    + " where rr.runResult.competitionRun.stage.sport = :sport").setParameter("sport", s);
            int r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionSport: "+r+" result entries deleted");
            // deleting run results
            q = em.createQuery("delete from RunResultEntity rr"
                    + " where rr.competitionRun.stage.sport = :sport").setParameter("sport", s);
            r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionSport: "+r+" run results deleted");
            // deleting runs
            q = em.createQuery("delete from CompetitionRunEntity r"
                    + " where r.stage.sport = :sport").setParameter("sport", s);
            r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionSport: "+r+" runs deleted");
            // deleting stages
            q = em.createQuery("delete from CompetitionStageEntity s "
                    + "where s.sport = :sport").setParameter("sport", s);
            r=q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionSport: "+r+" stages deleted");
            // deleting sport
            em.remove(s);
            System.out.println("DBHelper.deleteCompetitionSport: competition sport has been removed");
        } catch (Exception ex) {
            System.err.println("DBHelper.deleteCompetitionSport: failed to remove competition sport.");
            System.err.println("Reason: "+ex.getLocalizedMessage());
            db.rollback();
        }
    }

    public void deleteCompetitionStage(DBUtils db, CompetitionStage s) {
        EntityManager em = db.getEntityManager(true);
        try {
            // deleting  result entries
            Query q = em.createQuery("delete from RunResultEntryEntity r"
                    + " where r.runResult.competitionRun.stage = :stage").setParameter("stage", s);
            int r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionStage: "+r+" result entries deleted");
            // deleting run results
            q = em.createQuery("delete from RunResultEntity r"
                    + " where r.competitionRun.stage = :stage").setParameter("stage", s);
            r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionStage: "+r+" run results deleted");
            // deleting runs
            q = em.createQuery("delete from CompetitionRunEntity r"
                    + " where r.stage = :stage").setParameter("stage", s);
            r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionStage: "+r+" runs deleted");
            // deleting stage
            em.remove(s);
            System.out.println("DBHelper.deleteCompetitionStage: competition stage has been removed");
        } catch (Exception ex) {
            System.err.println("DBHelper.deleteCompetitionStage: failed to remove competition stage.");
            System.err.println("Reason: "+ex.getLocalizedMessage());
            db.rollback();
        }
    }

    public void deleteCompetitionRun(DBUtils db, CompetitionRun s) {
        EntityManager em = db.getEntityManager(true);
        try {
            // deleting result entries
            Query q = em.createQuery("delete from RunResultEntryEntity r"
                    + " where r.runResult.competitionRun = :run").setParameter("run", s);
            int r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionRun: "+r+" result entries deleted");
            // deleting run results
            q = em.createQuery("delete from RunResultEntity r"
                    + " where r.competitionRun = :run").setParameter("run", s);
            r = q.executeUpdate();
            System.out.println("DBHelper.deleteCompetitionRun: "+r+" run results deleted");
            // deleting run
            em.remove(s);
            System.out.println("DBHelper.deleteCompetitionRun: competition run has been removed");
        } catch (Exception ex) {
            System.err.println("DBHelper.deleteCompetitionRun: failed to remove competition run.");
            System.err.println("Reason: "+ex.getLocalizedMessage());
            db.rollback();
        }
    }

    public CompetitionRun createCompetitionRun(DBUtils db, CompetitionStage s) {
        EntityManager em = db.getEntityManager(true);
        try {
            CompetitionRun run = new CompetitionRunEntity();
            run.setStage((CompetitionStageEntity)s);
            em.persist(run);
            System.out.println("DBHelper.createCompetitionRun: competition run has been created");
            return run;
        } catch (Exception ex) {
            System.err.println("DBHelper.createCompetitionRun: failed to create competition run.");
            System.err.println("Reason: "+ex.getLocalizedMessage());
            db.rollback();
        }
        return null;
    }

    public RunResult addRunResult(DBUtils db, RunResult res, List<RunResultEntryEntity> entries) {
        EntityManager em = db.getEntityManager(true);
        try {
            res.setResultEntries(new ArrayList<RunResultEntryEntity>());
            em.persist(res);
            for(RunResultEntryEntity r : entries) {
                r.setRunResult((RunResultEntity)res);
                em.persist(r);
            }
            // merge all entries
        } catch (Exception ex) {
            System.err.println("DBHelper.addRunResult: failed to persist run result.");
            System.err.println("Reason: "+ex.getLocalizedMessage());
            db.rollback();
        }
        return null;
    }
}
